package gu.sql2java.excel.config;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.xssf.usermodel.XSSFFont;

import com.facebook.swift.codec.ThriftField;
import com.facebook.swift.codec.ThriftStruct;
import com.gitee.l0km.com4j.basex.bean.jdk.descriptor.BaseRowPropertyDescriptor;
import com.gitee.l0km.com4j.basex.bean.jdk.descriptor.MapPropertyDescriptor;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.common.primitives.Primitives;
import com.google.common.primitives.Shorts;
import gu.sql2java.BaseRow;
import gu.sql2java.RowMetaData;
import gu.sql2java.UnnameRow;
import gu.sql2java.excel.CustomBeanParser;
import gu.sql2java.excel.annotations.ExcelColumn;
import gu.sql2java.excel.annotations.ExcelColumns;
import gu.sql2java.excel.annotations.ExcelSheet;
import gu.sql2java.excel.utils.FieldUtils;
import gu.sql2java.utils.CaseSupport;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.gitee.l0km.com4j.basex.bean.BeanPropertySupport.isEmpty;
import static com.gitee.l0km.com4j.basex.bean.jdk.BeanPropertySupportImpl.BEAN_SUPPORT;
import static com.google.common.base.MoreObjects.firstNonNull;
import static gu.sql2java.excel.utils.MethodSupport.mergeAnnotaionFields;
import static gu.sql2java.utils.CaseSupport.*;

/**
 * Excel表输出全局配置
 * @author guyadong
 *
 */
public class SheetConfig {
	/** excel Sheet名字 */
	private String sheetName = "exportedExcel";
    
    /** excel Sheet 标题 */
	private String title = "";
	/**
	 * 标题字体名
	 */
	private String titleFontName = XSSFFont.DEFAULT_FONT_NAME;
	/**
	 * 标题字体高度
	 */
	private short titleFontHeight = 32;
	/**
	 * 标题字体颜色,参见 {@link org.apache.poi.ss.usermodel.IndexedColors}
	 */
	private IndexedColors titleFontColor = IndexedColors.BLACK;
    /**
     * 标题单元背景填充颜色,参见 {@link org.apache.poi.ss.usermodel.IndexedColors}
     */
	private IndexedColors titleFillColor =  IndexedColors.WHITE;
    /**
     * 标题对齐方式
     */
    private  HorizontalAlignment titleHorizontalAlign = HorizontalAlignment.CENTER;

    /**
     * 首行(字段名)字体名
     */
	private String headerFontName = XSSFFont.DEFAULT_FONT_NAME;
    /**
     * 首行(字段名)字体高度
     */
	private short headerFontHeight = 16;
	/**
	 * 首行(字段名)字体颜色,参见 {@link org.apache.poi.ss.usermodel.IndexedColors}
	 */
	private IndexedColors headerFontColor = IndexedColors.BLACK;
    /**
     * 首行(字段名)单元背景填充颜色,参见 {@link org.apache.poi.ss.usermodel.IndexedColors}
     */
	private IndexedColors headerFillColor = IndexedColors.GREY_25_PERCENT;
    /**
     * 首行(字段名)对齐方式
     */
    private  HorizontalAlignment headerHorizontalAlign = HorizontalAlignment.CENTER;

	/**
	 * 字体设置:标题行字体加粗
	 */
	private boolean firstBold = true;
	/**
	 * 默认字体高度
	 */
	private short fontHeight =16;
	/**
	 * 默认字体名
	 */
	private String fontName= XSSFFont.DEFAULT_FONT_NAME;
	/**
	 * 默认字体颜色,参见 {@link org.apache.poi.ss.usermodel.IndexedColors}
	 */
	private IndexedColors fontColor = IndexedColors.BLACK;
    /**
     * 默认单元背景填充颜色,参见 {@link org.apache.poi.ss.usermodel.IndexedColors}
     */
	private IndexedColors fillColor = IndexedColors.WHITE;
    /**
	 * 默认导出字段水平对齐方式
	 */
	private HorizontalAlignment horizontalAlign= HorizontalAlignment.CENTER;

	/**
     * 默认整数(Integer,Long,Short)格式
     */
	private String integralFormat = "0";
    /**
     * ({@link java.util.Date})日期时间格式, 如: yyyy-MM-dd HH:mm:ss
     */
	private String dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
    /**
     * ({@link java.sql.Date})日期格式, 如: yyyy-MM-dd
     */
	private String dateFormat = "yyyy-MM-dd";
    /**
     * ({@link java.sql.Time})时间格式, 如: HH:mm:ss
     */
	private String timeFormat = "HH:mm:ss";
    /**
     * ({@link java.sql.Timestamp})时间戳格式, 如: yyyy-MM-dd HH:mm:ss
     */
	private String timestampFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
    /**
     * 默认BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
     */
	private int scale = -1;
    /**
     * 默认BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
     */
	private int roundingMode = BigDecimal.ROUND_HALF_EVEN;
    /**
	 * 导出时在excel中每个列的最大宽度, 单位为字符
	 */
	private int maxWidth= 32;
	/**
     * 导出时在excel中每个列的最大高度, 单位为字符
     */
	private int maxHeight= 0;

    /**
     * 当值为空时,字段的默认值
     */
	private String defaultValue = "";

    /**
	 * 字段输出白名单,在此名单中的字段会被输出,同时指定白名单和黑名单时以白名单为准
	 */
	private Set<String> includeColumns = Sets.newHashSet();
	/**
	 * 字段输出黑名单,在此名单中的字段不会被输出,同时指定白名单和黑名单时以白名单为准
	 */
	private Set<String> excludeColumns = Sets.newHashSet();
	/**
	 * 隐藏字段名单,指定任何情况下都不输出的字段列表
	 * 在此名单中的字段,不论{@link #includeColumns}{@link #excludeColumns}如何设置都不会被输出
	 */
	private Set<String> hideColumns = Sets.newHashSet();
    /**
	 * 默认的字段输出白名单,此字段用于服务方法中保存的动态输出EXCEL字段名
	 */
	private Set<String> defaultIncludeColumns = Sets.newHashSet();
	/**
	 * 动态输出Excel中的字段名列表,此字段用于保存前端提供的动态输出字段名
	 */
	private Set<String> dynamicExcelNames = Sets.newHashSet();
    /**
     * 导出的文件名前缀
     */
	private String fileNamePrefix = "";
	/**
	 * 字段名(nestedName)--字段配置映射
	 */
	private LinkedHashMap<String, PropertyConfig> columnConfigs = Maps.newLinkedHashMap();

	private Class<?> beanClass = Object.class;
    /**
     * 自定义导入数据转换实现类
     * @since 3.29.0
     */
    private CustomBeanParser customImporter = null;
    /**
     * 阻塞队列读取超时(秒)<br>
     * 当从阻塞队列({@link BlockingQueue})中读取数据超过此值即判定队列中不再有数据
     */
	private int queueTimeout = 10;
	/**
	 * 预设置的输出记录总数,用于阻塞队列方式读取记录时判断队列是否结束
	 */
	private long totalRowCount = -1L;
	/**
	 * 默认构造方法
	 */
	public SheetConfig() {
	}
	/**
	 * 构造方法<br>
	 * 支持从注解{@link ExcelSheet},{@link ExcelColumn}获取配置参数
	 * @param sheetAnnot
	 * @param columnAnnots
	 */
	public SheetConfig(ExcelSheet sheetAnnot,ExcelColumn...columnAnnots) {
		if(null != sheetAnnot){
			sheetName = sheetAnnot.sheetName();
			fileNamePrefix = sheetAnnot.fileNamePrefix();
			title =  sheetAnnot.title();
			setTitleFontName(sheetAnnot.titleFontName());
			titleFontHeight = sheetAnnot.titleFontHeight();
			setTitleFillColor(sheetAnnot.titleFontColor());
			setTitleFillColor(sheetAnnot.titleFillColor());
			setTitleHorizontalAlign(sheetAnnot.titleHorizontalAlign());
			setHeaderFontName(sheetAnnot.headerFontName());
			headerFontHeight = sheetAnnot.headerFontHeight();
			setHeaderFontColor(sheetAnnot.headerFontColor());
			setHeaderFillColor(sheetAnnot.headerFillColor());
			setHeaderHorizontalAlign(sheetAnnot.headerHorizontalAlign());
			firstBold = sheetAnnot.firstBold();
			fontHeight = sheetAnnot.fontHeight();
			setFontName(sheetAnnot.fontName());
			setFontColor(sheetAnnot.fontColor());
			setFillColor(sheetAnnot.fillColor());
			setHorizontalAlign(sheetAnnot.horizontalAlign());
			integralFormat = sheetAnnot.integralFormat();
			dateTimeFormat = sheetAnnot.dateTimeFormat();
			dateFormat = sheetAnnot.dateFormat();
			timeFormat = sheetAnnot.timeFormat();
			timestampFormat = sheetAnnot.timestampFormat();
			scale = sheetAnnot.scale();
			roundingMode = sheetAnnot.roundingMode();
			maxWidth = sheetAnnot.maxWidth();
			maxHeight = sheetAnnot.maxHeight();
			defaultValue = sheetAnnot.defaultValue();
			/** 
			 * setIncludeColumns调用放在setExcludeColumns之后,
			 * 以确保同时指定白名单和黑名单时以白名单为准
			 */			
			setExcludeColumns(sheetAnnot.excludeColumns());
			setIncludeColumns(sheetAnnot.includeColumns());
			setHideColumns(sheetAnnot.hideColumns());
			setDefaultIncludeColumns(sheetAnnot.defaultIncludeColumns());
			if(!Object.class.equals(sheetAnnot.beanClass())){
				setBeanClass(sheetAnnot.beanClass());	
			}
			if(!CustomBeanParser.class.equals(sheetAnnot.customImporterClass())){
				setCustomImporter(sheetAnnot.customImporterClass());	
			}
		}
		if(null != columnAnnots){			
			Iterable<ExcelColumn>columns=Iterables.filter(Arrays.asList(columnAnnots), 
					Predicates.and(Predicates.notNull(),a->!a.columnName().isEmpty()));
			Iterable<PropertyConfig> columnConfigs = Iterables.transform(columns, c->new ExcelPropertyConfig(c));
			addColumns(columnConfigs);
		}
	}
	/**
	 * 构造方法,支持从服务方法中获取注解完成配置<br>
	 * 如果方法中定义了{@link ExcelSheet}注解,注解中定义了beanClass,则调用{@link #SheetConfig(Class)}构造方法基于JavaBean类型构造当前实例，再调用{@link #SheetConfig(Method)}构造方法基于方法构造一个临时实例,
	 * 将临时实例合并到当前实例
	 * @param method
	 */
	public SheetConfig(Method method){
		this(checkNotNull(method,"method is null").getAnnotation(ExcelSheet.class),getExcelColumns(method));
		ExcelSheet excelSheet = method.getAnnotation(ExcelSheet.class);
		Class<?> beanClass;
		if(null != excelSheet && null != (beanClass = excelSheet.beanClass())){
			if(!Object.class.equals(beanClass)){
				/** 方法定义优先,所以这里有两次merge */
				SheetConfig other = new SheetConfig(beanClass).merge(this);	
				merge(other);
			}			
		}
	}
	/**
	 * 构造方法,支持普通JavaBean类型
	 * @param beanClass
	 */
	public SheetConfig(Class<?> beanClass){
		this(checkNotNull(beanClass,"beanClass is null").getAnnotation(ExcelSheet.class),new ExcelColumn[0]);
		this.beanClass = beanClass;
		setColumnConfigs(excelPropertyConfigsOf(beanClass));
		/** 将类定义上获取的ExcelColumn合并到当前对象中的字段配置集合中 */
		Lists.transform(Arrays.asList(fetchExcelColumns(beanClass)),c->(PropertyConfig)new ExcelPropertyConfig(c,c.columnName()))
			.forEach(c->replaceOrAddConfig(c));
	}
	/**
	 * 构造方法支持 {@link gu.sql2java.UnnameRow}记录
	 * @param metaData
	 */
	public SheetConfig(RowMetaData metaData){
		this();
		setColumnConfigs(excelPropertyConfigsOf(metaData));
	}
	/**
	 * 构造方法,支持普通JavaBean实例
	 * @param bean
	 */
	public SheetConfig(Object bean){
		this(checkNotNull(bean,"beanClass is null").getClass().getAnnotation(ExcelSheet.class),new ExcelColumn[0]);
		this.beanClass = bean.getClass();
		setColumnConfigs(excelPropertyConfigsOf(bean));
	}
	/**
	 * 构造方法,支持Map类型的记录对象包括{@link com.alibaba.fastjson.JSONObject}
	 * @param iterable
	 */
	@SuppressWarnings("rawtypes")
	public SheetConfig(Iterable<Map> iterable){
		this(iterable,null);
	}
	/**
	 * 构造方法,支持Map类型的记录对象包括{@link com.alibaba.fastjson.JSONObject}
	 * @param iterable
	 * @param includeCoumns
	 */
	@SuppressWarnings("rawtypes")
	public SheetConfig(Iterable<Map> iterable,Iterable<String> includeCoumns){
		this(checkNotNull(iterable,"beanClass is null").getClass().getAnnotation(ExcelSheet.class),new ExcelColumn[0]);
		includeCoumns = Iterables.filter(firstNonNull(includeCoumns, Collections.emptyList()),Predicates.notNull());
		if(isEmpty(includeCoumns)){
			setColumnConfigs(excelPropertyConfigsOf(iterable));
		}else{
			setColumnConfigs(Iterables.transform(includeCoumns, n->new ExcelPropertyConfig(n)));
		}
	}
	public String getSheetName() {
		return sheetName;
	}

	public SheetConfig setSheetName(String sheetName) {
		if(!isEmpty(sheetName)){
			this.sheetName = sheetName;
		}
		return this;
	}

	/**
	 * @return fileNamePrefix
	 */
	public String getFileNamePrefix() {
		return fileNamePrefix;
	}
	/**
	 * @param fileNamePrefix 要设置的 fileNamePrefix
	 * @return 当前对象
	 */
	public SheetConfig setFileNamePrefix(String fileNamePrefix) {
		if(!isEmpty(fileNamePrefix)){
			this.fileNamePrefix = fileNamePrefix;
		}
		return this;
	}
	public String getTitle() {
		return title;
	}
	public SheetConfig setTitle(String title) {
		if(!isEmpty(title)){
			this.title = title;
		}
		return this;
	}
	public String getTitleFontName() {
		return titleFontName;
	}
	public SheetConfig setTitleFontName(String titleFontName) {
		if(!isEmpty(titleFontName)){
			this.titleFontName = titleFontName;
		}
		return this;
	}
	
	public short getTitleFontHeight() {
		return titleFontHeight;
	}
	
	public SheetConfig setTitleFontHeight(short titleFontHeight) {
		this.titleFontHeight = titleFontHeight;
		return this;
	}
	public SheetConfig setTitleFontHeight(Integer titleFontHeight) {
		if(null != titleFontHeight){
			this.titleFontHeight = titleFontHeight.shortValue();
		}
		return this;
	}

	public IndexedColors getTitleFontColor() {
		return titleFontColor;
	}
	public SheetConfig setTitleFontColor(IndexedColors titleFontColor) {
		if(null != titleFontColor){
			this.titleFontColor = titleFontColor;
		}
		return this;
	}
	public SheetConfig setTitleFontColor(String titleFontColor) {
		try{
			this.titleFontColor = IndexedColors.valueOf(titleFontColor);
		}catch (Exception e) {
			// DO NOTHING
		}
		return this;
	}
	
	public IndexedColors getTitleFillColor() {
		return titleFillColor;
	}
	public SheetConfig setTitleFillColor(IndexedColors titleFillColor) {
		if(null != titleFillColor){
			this.titleFillColor = titleFillColor;
		}
		return this;
	}
	public SheetConfig setTitleFillColor(String titleFillColor) {
		try{
			this.titleFillColor = IndexedColors.valueOf(titleFillColor);
		}catch (Exception e) {
			// DO NOTHING
		}
		return this;
	}
	
	/**
	 * @return titleHorizontalAlign
	 */
	public HorizontalAlignment getTitleHorizontalAlign() {
		return titleHorizontalAlign;
	}
	/**
	 * @param titleHorizontalAlign 要设置的 titleHorizontalAlign
	 * @return 当前对象
	 */
	public SheetConfig setTitleHorizontalAlign(HorizontalAlignment titleHorizontalAlign) {
		if(null != titleHorizontalAlign){
			this.titleHorizontalAlign = titleHorizontalAlign;			
		}
		return this;
	}
	/**
	 * @param titleHorizontalAlign 要设置的 titleHorizontalAlign
	 * @return 当前对象
	 */
	public SheetConfig setTitleHorizontalAlign(String titleHorizontalAlign) {
		try{
			this.titleHorizontalAlign = HorizontalAlignment.valueOf(titleHorizontalAlign);			
		}catch (Exception e) {
			// DO NOTHING
		}
		return this;
	}
	public String getHeaderFontName() {
		return headerFontName;
	}
	public SheetConfig setHeaderFontName(String headerFontName) {
		if(!isEmpty(headerFontName)){
			this.headerFontName = headerFontName;
		}
		return this;
	}
	public short getHeaderFontHeight() {
		return headerFontHeight;
	}
	public SheetConfig setHeaderFontHeight(short headerFontHeight) {
		this.headerFontHeight = headerFontHeight;
		return this;
	}
	public SheetConfig setHeaderFontHeight(Integer headerFontHeight) {
		if(null != headerFontHeight){
			this.headerFontHeight = headerFontHeight.shortValue();			
		}
		return this;
	}
	
	public IndexedColors getHeaderFontColor() {
		return headerFontColor;
	}
	public SheetConfig setHeaderFontColor(IndexedColors headerFontColor) {
		if(null != headerFontColor){
			this.headerFontColor = headerFontColor;
		}
		return this;
	}
	public SheetConfig setHeaderFontColor(String headerFontColor) {
		try{
			this.headerFontColor = IndexedColors.valueOf(headerFontColor);
		}catch (Exception e) {
			// DO NOTHING
		}
		return this;
	}
	
	public IndexedColors getHeaderFillColor() {
		return headerFillColor;
	}
	public SheetConfig setHeaderFillColor(IndexedColors headerFillColor) {
		if(null != headerFillColor){
			this.headerFillColor = headerFillColor;
		}
		return this;
	}
	
	public SheetConfig setHeaderFillColor(String headerFillColor) {
		try{
			this.headerFillColor = IndexedColors.valueOf(headerFillColor);
		}catch (Exception e) {
			// DO NOTHING
		}
		return this;
	}
	
	/**
	 * @return headerHorizontalAlign
	 */
	public HorizontalAlignment getHeaderHorizontalAlign() {
		return headerHorizontalAlign;
	}
	/**
	 * @param headerHorizontalAlign 要设置的 headerHorizontalAlign
	 * @return 当前对象
	 */
	public SheetConfig setHeaderHorizontalAlign(HorizontalAlignment headerHorizontalAlign) {
		if(null != headerHorizontalAlign){
			this.headerHorizontalAlign = headerHorizontalAlign;
		}
		return this;
	}
	/**
	 * @param headerHorizontalAlign 要设置的 headerHorizontalAlign
	 * @return 当前对象
	 */
	public SheetConfig setHeaderHorizontalAlign(String headerHorizontalAlign) {
		try{
			this.headerHorizontalAlign = HorizontalAlignment.valueOf(headerHorizontalAlign);
		}catch (Exception e) {
			// DO NOTHING
		}
		return this;
	}
	public boolean isFirstBold() {
		return firstBold;
	}

	public SheetConfig setFirstBold(boolean firstBold) {
		this.firstBold = firstBold;
		return this;
	}
	public SheetConfig setFirstBold(Boolean firstBold) {
		if(null != firstBold){
			this.firstBold = firstBold;
		}
		return this;
	}

	public short getFontHeight() {
		return fontHeight;
	}

	public SheetConfig setFontHeight(short fontHeight) {
		this.fontHeight = fontHeight;
		return this;
	}
	public SheetConfig setFontHeight(Integer fontHeight) {
		if(null != fontHeight){
			this.fontHeight =fontHeight.shortValue();
		}
		return this;
	}

	public String getFontName() {
		return fontName;
	}

	public SheetConfig setFontName(String fontName) {
		if(!isEmpty(fontName)){
			this.fontName = fontName;
		}
		return this;
	}

	public IndexedColors getFontColor() {
		return fontColor;
	}

	public SheetConfig setFontColor(IndexedColors fontColor) {
		if(null != fontColor){
			this.fontColor = fontColor;
		}
		return this;
	}
	public SheetConfig setFontColor(String fontColor) {
		try{
			this.fontColor = IndexedColors.valueOf(fontColor);
		}catch (Exception e) {
			// DO NOTHING
		}
		return this;
	}

	public IndexedColors getFillColor() {
		return fillColor;
	}
	public SheetConfig setFillColor(IndexedColors fillColor) {
		if(null != fillColor){
			this.fillColor = fillColor;
		}
		return this;
	}
	public SheetConfig setFillColor(String fillColor) {
		try{
			this.fillColor = IndexedColors.valueOf(fillColor);
		}catch (Exception e) {
			// DO NOTHING
		}
		return this;
	}
	public HorizontalAlignment getHorizontalAlign() {
		return horizontalAlign;
	}
	public SheetConfig setHorizontalAlign(HorizontalAlignment horizontalAlign) {
		if(null != horizontalAlign){
			this.horizontalAlign = horizontalAlign;
		}
		return this;
	}
	public SheetConfig setHorizontalAlign(String horizontalAlign) {
		try{
			this.horizontalAlign = HorizontalAlignment.valueOf(horizontalAlign);
		}catch (Exception e) {
			// DO NOTHING
		}
		return this;
	}
	public String getIntegralFormat() {
		return integralFormat;
	}
	public SheetConfig setIntegralFormat(String integralFormat) {
		if(!isEmpty(integralFormat)){
			this.integralFormat = integralFormat;
		}
		return this;
	}
	public String getDateTimeFormat() {
		return dateTimeFormat;
	}
	public SheetConfig setDateTimeFormat(String dateTimeFormat) {
		if(!isEmpty(dateTimeFormat)){
			this.dateTimeFormat = dateTimeFormat;
		}
		return this;
	}
	public String getDateFormat() {
		return dateFormat;
	}

	public SheetConfig setDateFormat(String dateFormat) {
		if(!isEmpty(dateFormat)){
			this.dateFormat = dateFormat;
		}
		return this;
	}

	public String getTimeFormat() {
		return timeFormat;
	}
	public SheetConfig setTimeFormat(String timeFormat) {
		if(!isEmpty(timeFormat)){
			this.timeFormat = timeFormat;
		}
		return this;
	}
	public String getTimestampFormat() {
		return timestampFormat;
	}
	public SheetConfig setTimestampFormat(String timestampFormat) {
		if(!isEmpty(timestampFormat)){
			this.timestampFormat = timestampFormat;
		}
		return this;
	}
	public int getScale() {
		return scale;
	}

	public SheetConfig setScale(int scale) {
		this.scale = scale;
		return this;
	}

	public int getRoundingMode() {
		return roundingMode;
	}

	public SheetConfig setRoundingMode(int roundingMode) {
		this.roundingMode = roundingMode;
		return this;
	}

	public int getMaxWidth() {
		return maxWidth;
	}
	public SheetConfig setMaxWidth(int maxWidth) {
		this.maxWidth = maxWidth;
		return this;
	}
	public SheetConfig setMaxWidth(Integer maxWidth) {
		if(null != maxWidth){
			this.maxWidth = maxWidth;
		}
		return this;
	}
	public int getMaxHeight() {
		return maxHeight;
	}

	public SheetConfig setMaxHeight(int maxHeight) {
		this.maxHeight = maxHeight;
		return this;
	}
	public SheetConfig setMaxHeight(Integer maxHeight) {
		if(null != maxHeight){
			this.maxHeight = maxHeight;
		}
		return this;
	}

	public String getDefaultValue() {
		return defaultValue;
	}

	public SheetConfig setDefaultValue(String defaultValue) {
		this.defaultValue = defaultValue;
		return this;
	}

	/**
	 * @return 字段输出白名单
	 */
	public Set<String> getIncludeColumns() {
		return includeColumns;
	}

	/**
	 * @see #setIncludeColumns(Iterable)
	 */
	public SheetConfig setIncludeColumns(Set<String> includeColumns) {
		return setIncludeColumns((Iterable<String>)includeColumns);
	}
	/**
	 * 指定要输出的字段(白名单),在此名单中的字段才会被输出,
	 * 此方法也定义了字段输出顺序,
	 * @param includeColumns
	 * @return 当前对象
	 */
	public SheetConfig setIncludeColumns(Iterable<String> includeColumns) {
		if(!isEmpty(includeColumns)){
			Iterable<String> filtered = Iterables.filter(includeColumns, s->!isEmpty(s));
			if(!Iterables.isEmpty(filtered)){
				this.includeColumns = Sets.newLinkedHashSet(filtered);
			}
		}
		return this;
	}
	/**
	 * @see #setIncludeColumns(Iterable)
	 */
	public SheetConfig setIncludeColumns(String ... includeColumns) {
		if(!isEmpty(includeColumns)){
			setIncludeColumns(Arrays.asList(includeColumns));
		}
		return this;
	}
	/**
	 * @return 字段输出黑名单
	 */
	public Set<String> getExcludeColumns() {
		return excludeColumns;
	}

	/**
	 * @see #setExcludeColumns(Iterable) 
	 */
	public SheetConfig setExcludeColumns(Set<String> excludeColumns) {
		return setExcludeColumns((Iterable<String>)excludeColumns);
	}
	/**
	 * 指定输出时要排序的字段(黑名单),在此名单中的字段不被输出<br>
	 * @param excludeColumns
	 * @return 当前对象
	 */
	public SheetConfig setExcludeColumns(Iterable<String> excludeColumns) {
		if(!isEmpty(excludeColumns)){
			Iterable<String> filtered = Iterables.filter(excludeColumns, s->!isEmpty(s));
			if(!Iterables.isEmpty(filtered)){				
				this.excludeColumns = Sets.newLinkedHashSet(filtered);
			}
		}
		return this;
	}
	/**
	 * @see #setExcludeColumns(Iterable) 
	 */
	public SheetConfig setExcludeColumns(String ... excludeColumns) {
		if(!isEmpty(excludeColumns)){
			setExcludeColumns(Arrays.asList(excludeColumns));
		}
		return this;
	}
	/**
	 * 返回所有嵌入字段
	 * @since 3.29.4
	 */
	public List<String> getNestedNames() {
		return getAvailableColumns().keySet()
				.stream().filter(s->s.indexOf('.')>=0).collect(Collectors.toList());
	}
	/**
	 * @return hideColumns
	 */
	public Set<String> getHideColumns() {
		return hideColumns;
	}
	/**
	 * @param hideColumns 要设置的 hideColumns
	 * @return 当前对象
	 */
	public SheetConfig setHideColumns(Set<String> hideColumns) {
		if(!isEmpty(hideColumns)){
			Iterable<String> filtered = Iterables.filter(hideColumns, s->!isEmpty(s));
			if(!Iterables.isEmpty(filtered)){				
				this.hideColumns = Sets.newLinkedHashSet(filtered);
			}
		}
		return this;
	}
	/**
	 * @param hideColumns 要设置的 hideColumns
	 * @see #setHideColumns(Iterable) 
	 */
	public SheetConfig setHideColumns(Iterable<String> hideColumns) {
		if(!isEmpty(hideColumns)){			
			setHideColumns(Sets.newLinkedHashSet(hideColumns));
		}
		return this;
	}
	/**
	 * @param hideColumns 要设置的 hideColumns
	 * @return 当前对象
	 */
	public SheetConfig setHideColumns(String... hideColumns) {
		if(!isEmpty(hideColumns)){
			setHideColumns(Arrays.asList(hideColumns));
		}
		return this;
	}
	/**
	 * @return defaultIncludeColumns
	 */
	public Set<String> getDefaultIncludeColumns() {
		return defaultIncludeColumns;
	}
	/**
	 * @param defaultIncludeColumns 要设置的 defaultIncludeColumns
	 */
	public SheetConfig setDefaultIncludeColumns(Set<String> defaultIncludeColumns) {
		if(!isEmpty(defaultIncludeColumns)){
			Iterable<String> filtered = Iterables.filter(defaultIncludeColumns, s->!isEmpty(s));
			if(!Iterables.isEmpty(filtered)){				
				this.defaultIncludeColumns = Sets.newLinkedHashSet(filtered);
			}
		}
		return this;
	}
	/**
	 * @param defaultIncludeColumns 要设置的 defaultIncludeColumns
	 */
	public void setDefaultIncludeColumns(Iterable<String> defaultIncludeColumns) {
		if(!isEmpty(defaultIncludeColumns)){
			setDefaultIncludeColumns(Sets.newLinkedHashSet(defaultIncludeColumns));
		}
	}
	/**
	 * @param defaultIncludeColumns 要设置的 defaultIncludeColumns
	 */
	public void setDefaultIncludeColumns(String... defaultIncludeColumns) {
		if(!isEmpty(defaultIncludeColumns)){
			setDefaultIncludeColumns(Arrays.asList(defaultIncludeColumns));
		}
	}
	/**
	 * @return dynamicExcelNames
	 * @since 3.26.0
	 */
	public Set<String> getDynamicExcelNames() {
		return dynamicExcelNames;
	}
	/**
	 * @param dynamicExcelNames 要设置的 dynamicExcelNames
	 * @since 3.26.0
	 */
	public void setDynamicExcelNames(Set<String> dynamicExcelNames) {
		if(null != dynamicExcelNames) {
			this.dynamicExcelNames = dynamicExcelNames;
		}
	}
	private void injectDynamicColumn(Collection<PropertyConfig> exported) {
		if(!dynamicExcelNames.isEmpty()) {
			dynamicExcelNames.stream()
			.map(this::configByExcelName)
			.filter(c->null!=c).forEach(c->exported.add(c));
		}
	}
	/**
	 * 返回需要导出的字段列表<br>
	 * 如果字段输出白名单({@link #includeColumns})不为空则返回此名单中的字段配置对象
	 * 如果字段输出黑名单{@link #excludeColumns}不为空则返回不在此名单中的所有字段配置对象
	 * 否则返回所有字段;
	 */
	public Iterable<PropertyConfig> getExportedColumnConfigs() {
		Collection<PropertyConfig> exported;
		if(!includeColumns.isEmpty()){
			exported = includeColumns.stream().map(this::configOf).collect(Collectors.toCollection(LinkedHashSet::new));
			injectDynamicColumn(exported);
		}else if(!excludeColumns.isEmpty()){
	        // 使用Stream将excludeColumns转为snake-case name
	        Set<String> snakeExcludeNames = excludeColumns.stream()
	        		.map(CaseSupport::toNestedSnakecase).collect(Collectors.toSet());
			exported = columnConfigs.entrySet().stream()
				.filter(entry->!snakeExcludeNames.contains(entry.getKey()))
				.map(Map.Entry::getValue)
				.collect(Collectors.toCollection(LinkedHashSet::new));
			injectDynamicColumn(exported);
		}else if (!defaultIncludeColumns.isEmpty()){
			exported = defaultIncludeColumns.stream().map(this::configOf).collect(Collectors.toCollection(LinkedHashSet::new));
		}else {
			exported =  columnConfigs.values();
		}
		// 使用Stream将hideColumns转为snake-case name
		Set<String> snakeHideNames = hideColumns.stream()
				.map(CaseSupport::toNestedSnakecase).collect(Collectors.toSet());
		LinkedHashSet<PropertyConfig> nohidded = exported.stream()
				.filter(c->!snakeHideNames.contains(toNestedSnakecase(c.getNestedName()))).collect(Collectors.toCollection(LinkedHashSet::new));
		return nohidded;
	}
	/**
	 * 返回所有字段配置对象
	 */
	public Iterable<PropertyConfig> getColumnConfigs() {
		return columnConfigs.values();
	}

	public SheetConfig setColumnConfigs(Iterable<PropertyConfig> columnConfigs) {
		this.columnConfigs = asLinkedHashMap(columnConfigs);
		return this;
	}
	public Class<?> getBeanClass() {
		return beanClass;
	}
	public void setBeanClass(Class<?> beanClass) {
		if(null != beanClass){
			this.beanClass = beanClass;
		}
	}
	
	public CustomBeanParser getCustomImporter() {
		return customImporter;
	}
	public void setCustomImporter(CustomBeanParser customImporter) {
		this.customImporter = customImporter;
	}
	public void setCustomImporter(Class<? extends CustomBeanParser> customImporterClass) {
		if(null != customImporterClass && !customImporterClass.isInterface()) {
			try {
				this.customImporter = customImporterClass.newInstance();
			} catch (Exception e) {
				Throwables.throwIfUnchecked(e);
				throw new RuntimeException(e);
			}
		}
	}
	public int getQueueTimeout() {
        return queueTimeout;
    }
    public void setQueueTimeout(int queueTimeout) {
        if(queueTimeout > 0){
            this.queueTimeout = queueTimeout;
        }
    }
    public long getTotalRowCount() {
        return totalRowCount;
    }
    public void setTotalRowCount(long totalRowCount) {
        if(totalRowCount > 0){
            this.totalRowCount = totalRowCount;
        }
    }
    public SheetConfig addColumns(Iterable<PropertyConfig> elements) {
		this.columnConfigs.putAll(asLinkedHashMap(elements));
		return this;
	}	
	public SheetConfig addColumns(PropertyConfig... elements) {
		if(null != elements){
			addColumns(Arrays.asList(elements));
		}
		return this;
	}
	private Iterable<PropertyConfig> asPropertyConfigs(Iterable<String> elements) {
		return  Iterables.transform(Iterables.filter(	firstNonNull(elements, Collections.emptyList()), Predicates.notNull()),
				e->new ExcelPropertyConfig(e));
	}	
	public SheetConfig addNestedColumns(Iterable<String> elements) {
		return addColumns(asPropertyConfigs(elements));
	}
	public SheetConfig addNestedColumns(String... elements) {
		if(null != elements){
			addNestedColumns(Arrays.asList(elements));
		}
		return this;
	}
	public SheetConfig addNestedColumn(String nestedName,String descName) {
		if(!isEmpty(nestedName)){
			ExcelPropertyConfig c = new ExcelPropertyConfig(nestedName,descName);
			c.setParent(this);
			columnConfigs.put(toNestedSnakecase(nestedName), c);
		}
		return this;
	}
	public boolean exists(String nestedName){
		return columnConfigs.containsKey(toNestedSnakecase(nestedName));
	}
	/**
	 * 根据属性路径名查找对应的属性配置对象，没找到返回{@code null}<br>
	 * @param nestedName
	 */
	public PropertyConfig configOrNullOf(String nestedName) {
		return columnConfigs.get(toNestedSnakecase(nestedName));
	}
	/**
	 * 根据EXCEL输出字段名名查找对应的属性配置对象，没找到返回{@code null},如果有多个匹配返回第一个
	 * @param excelName 为{@code null}或空返回{@code null}
	 * @since 3.26.0
	 */
	public PropertyConfig configByExcelName(String excelName) {
		return isEmpty(excelName)
				? null
				: columnConfigs.values()
				.stream()
				.filter(c->c.getColumnConfig().getName().equals(excelName))
				.findFirst()
				.orElse(null);
	}
	/**
	 * 根据属性路径名查找对应的属性配置对象，没找到返回则创建一个默认对象<br>
	 * 查找时如果没有找到完全匹配的值会自动切换节点名为驼峰命名格式或蛇形命名格式尝试
	 * @param nestedName
	 */
	public PropertyConfig configOf(String nestedName) {
		PropertyConfig config = configOrNullOf(nestedName);
		if(null == config){
			config = new ExcelPropertyConfig(nestedName);
			addColumns(config);
		}
		return config;
	}	
    /**
     * 根据注解获取最大行高
     */
    public short getRowHeight()
    {
        double maxHeight = 0;
        for (PropertyConfig os : this.columnConfigs.values())
        {
            ColumnConfig excel = os.getColumnConfig();
            maxHeight = Math.max(maxHeight, excel.getHeight());
        }
        return (short) (maxHeight * 20);
    }
    
    /**
     * 替换或增加指定的属性定义<br>
     * 如果当前对象存在{@code replaced}指定同名的字段定义,则将{@code replaced}合并到当前对象定义的字段定义中,
     * 否则将{@code replaced}作为新的字段定义添加到当前对象的字段配置容器中
     * @param replaced
     * @see ColumnConfig#merge(ColumnConfig)
     */
    private void replaceOrAddConfig(PropertyConfig replaced){
    	if(null != replaced){
    		String nestedName = replaced.getNestedName();
    		PropertyConfig config = configOrNullOf(nestedName);
    		if(null != config){
    			config.getColumnConfig().merge(replaced.getColumnConfig());
    		}else {
    			addColumns(replaced);
			}
    	}
    }
    /**
     * 将输入的Excel表配置与当前对象的合并<br>
     * 将{@code other}中有定义的Excel Sheet配置字段复制到当前对象的对应字段<br>
     * 将{@code other}定义的Excel Column配置对象({@link PropertyConfig})合并到当前的Column配置对象中,如果存在相同的对象,则以other的为准,<br>
     * 如果{@code other.beanClass}不为{@code null}则复制到当前对象的同名字段
     * @param other
     * @return 当前对象
     * @see gu.sql2java.excel.utils.MethodSupport#mergeAnnotaionFields(Class, Object, Object)
     * @see ColumnConfig#merge(ColumnConfig)
     */
    public SheetConfig merge(SheetConfig other){
    	if(null != other && other != this){
    		mergeAnnotaionFields(ExcelSheet.class, other, this);
    		other.columnConfigs.values().forEach(c->replaceOrAddConfig(c));
    		this.setQueueTimeout(other.queueTimeout);
    		this.setTotalRowCount(other.totalRowCount);
    	}
    	return this;
    }
    /**
     * 返回所有可输出字段及字段显示名称
     */
    public Map<String, String> getAvailableColumns(){
    	return columnConfigs.values()
	    	.stream()
	    	.collect(Collectors.toMap(PropertyConfig::getNestedName, PropertyConfig::getExcelColumnName));
    }
    
	/**
	 * 返回需要导出的字段列表<br>
	 * 如果字段输出黑名单{@link #excludeColumns}不为空则返回不在此名单中的所有字段配置对象
	 * 否则返回所有字段;
	 * @since 3.29.4
	 */
	public Collection<PropertyConfig> getImportColumnConfigs() {
		Collection<PropertyConfig> exported;
		if(!excludeColumns.isEmpty()){
	        // 使用Stream将excludeColumns转为snake-case name
	        Set<String> snakeExcludeNames = excludeColumns.stream()
	        		.map(CaseSupport::toNestedSnakecase).collect(Collectors.toSet());
			exported = columnConfigs.entrySet().stream()
				.filter(entry->!snakeExcludeNames.contains(entry.getKey()))
				.map(Map.Entry::getValue)
				.collect(Collectors.toCollection(LinkedHashSet::new));
			injectDynamicColumn(exported);
		}else {
			exported =  columnConfigs.values();
		}
		return exported;
	}
    /**
     * 返回所有EXCEL字段名称-PropertyConfig映射
     * @since 3.29.0
     */
    public Map<String, PropertyConfig> getExcelConfigs(){
    	return getImportColumnConfigs()
    			.stream()
    			.collect(Collectors.toMap(PropertyConfig::getExcelColumnName,p->p));
    }
    
	/**
	 * 根据{@link #defaultIncludeColumns}过滤返回默认输出的字段名和Excel导出时的列名
	 */
	public Map<String, String> getDefaultExportColumns() {
		return defaultIncludeColumns.stream()
			.map(this::configOrNullOf)
			.filter(p->null!=p)
			.collect(Collectors.toMap(
					PropertyConfig::getNestedName, 
					PropertyConfig::getExcelColumnName,
					 (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); },
					 LinkedHashMap::new));
	}
	/**
	 * 根据{@link #defaultIncludeColumns}过滤返回默认输出的字段名
	 */
	public Set<String> getDefaultExportColumnNames() {
		return getDefaultExportColumns().keySet();
	}
	/**
	 * 指定要输出的字段(白名单),如果输入参数为{@code null}或空,则使用{@link #getDefaultExportColumnNames()}的返回值
	 * @param includeColumns
	 * @return 当前对象
	 * @see #setIncludeColumns(Iterable)
	 */
	public SheetConfig setIncludeColumnsOrDefault(Iterable<String> includeColumns) {
		return setIncludeColumns(isEmpty(includeColumns)?getDefaultExportColumnNames():includeColumns);
	}
	public Set<String> getIncludeColumnsOrDefault(){
		if(getIncludeColumns().isEmpty()){
			return getDefaultIncludeColumns();
		}
		return getIncludeColumns();
	}
	private LinkedHashMap<String, PropertyConfig> asLinkedHashMap(Iterable<PropertyConfig> elements) {
		LinkedHashMap<String, PropertyConfig> m = Maps.newLinkedHashMap();
		if(!isEmpty(elements)){
			elements = Iterables.filter(elements, Predicates.notNull());
			Ordering<PropertyConfig> ordering = 
					Ordering.natural().onResultOf(c->c.getColumnConfig().getSort());
			List<PropertyConfig> list = elements instanceof List ? (List<PropertyConfig>)elements:Lists.newArrayList(elements);
			
			Collections.sort(list,ordering::compare);
			list.forEach(c->{
				c.setParent(SheetConfig.this);
				m.put(toNestedSnakecase(c.getNestedName()), c);
			});
		}
		return m;
	}
	private static ExcelColumn[] getExcelColumns(Method method){
		if(null != method){
			ExcelColumn annot = method.getAnnotation(ExcelColumn.class);
			if(null != annot){
				return new ExcelColumn[]{annot}; 
			}
			ExcelColumns annots = method.getAnnotation(ExcelColumns.class);
			if(null != annots){
				return annots.value();
			}
			
		}
		return null;
	}
	private static Iterable<PropertyDescriptor> fetchProperties(RowMetaData metaData){
		if(null != metaData){
			AtomicInteger count = new AtomicInteger(0);
			List<String> names=metaData.columnJavaNames;
			if(isEmpty(names)){
				names = metaData.columnNames;
			}
			return Lists.transform(names,name->{
				try {
					if(metaData.getterMethods.isEmpty()||metaData.setterMethods.isEmpty()){
						return new BaseRowPropertyDescriptor(metaData,name);						
					}
					return new PropertyDescriptor(name,
							metaData.getterMethods.get(count.get()),
							metaData.setterMethods.get(count.getAndIncrement()));
				} catch (IntrospectionException e) {
					throw new RuntimeException(e);
				}
			});
		}
		return Collections.emptyList();
	}
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static Iterable<PropertyDescriptor> fetchProperties(Map map){
		if(null != map){
			Iterable<PropertyDescriptor> props = Iterables.transform(map.entrySet(),(Map.Entry entry)->{
				try {
					if(null != entry.getKey()){	
						String key = String.valueOf(entry.getKey());
						Object value = entry.getValue();
						return new MapPropertyDescriptor(null != value ? value.getClass() : null,key);
					}
					return null;
					
				} catch (IntrospectionException e) {
					throw new RuntimeException(e);
				}
			});
			return Iterables.filter(props, Predicates.notNull());
		}
		return Collections.emptyList();
	}
	private static Iterable<PropertyDescriptor> fetchProperties(Class<?> beanClass){
		Iterable<PropertyDescriptor>output = Lists.newArrayList();
		if(null != beanClass){
			RowMetaData metaData = RowMetaData.getMetaDataUnchecked(beanClass);
			if(null != metaData){
				// 如果类型是BaseBean则根据 RowMetaData  读取字段信息
				output = fetchProperties(metaData);
			}else if(mayBeJavaBean(beanClass)){
				output = Lists.newArrayList(BEAN_SUPPORT.getProperties(beanClass, 3, true).values());
				if(null != beanClass.getAnnotation(ThriftStruct.class)){
					// 基于注解ThriftFiled#value字段排序
					Ordering<PropertyDescriptor> ordering = 
							Ordering.natural().onResultOf(c->{
								return Shorts.max(getValueOfThriftField(c.getReadMethod()),
										getValueOfThriftField(c.getReadMethod()),
										getValueOfThriftField(FieldUtils.getField(beanClass, c.getName(),true)));
							});
					Collections.sort((List<PropertyDescriptor>)output,ordering::compare);
				}
			}
		}
		return output;
	}
	@SuppressWarnings("rawtypes")
	private static Iterable<PropertyDescriptor> fetchProperties(Object bean){
		Iterable<PropertyDescriptor>output = Lists.newArrayList();
		if(null != bean){
			Class<? extends Object> beanClass = bean.getClass();
			if(bean instanceof Map){
				output = fetchProperties((Map)bean);
			}else if(!BaseRow.class.isAssignableFrom(beanClass)){
				output = fetchProperties(beanClass);
			} else{
				output = fetchProperties(((BaseRow)bean).fetchMetaData());
			}
		}
		return output;
		
	}
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static Iterable<PropertyConfig> fetchExcelPropertyConfig(Map m,List<PropertyConfig> output,String prefix){
		if(null != m && null != output){
			Iterable<Object> filtered = Iterables.filter(m.keySet(),
					Predicates.and(Predicates.notNull(),o->(o instanceof String) && !isEmpty(o)));
			filtered.forEach(k->{
				String name = (String)k;
				String nestedName = prefix + name;
				output.add(new ExcelPropertyConfig(nestedName,name));
				fetchExcelPropertyConfigs(m.get(k),output,nestedName + ".");
			});
		}
		return output;
	}
	@SuppressWarnings({ "rawtypes" })
	private static <T>List<PropertyConfig> fetchExcelPropertyConfigs0(
			T input,
			List<PropertyConfig> output,
			String prefix,
			Function<T,Iterable<PropertyDescriptor>>propsSupplier	){
		checkNotNull(null != prefix, "prefix is null");
		if(null != input && null != propsSupplier){
			Function<T, Class<?>>classSupplier;
			if(input instanceof Class){
				classSupplier = o->(Class<?>)o;
			}else if(input instanceof RowMetaData){
				classSupplier = o->((RowMetaData)o).beanType;
			}else {
				classSupplier = o->o.getClass();
			}
			
			Class<?> beanClass=classSupplier.apply(input);
			propsSupplier.apply(input).forEach(e->{
				String nestedName = prefix + e.getName();
				/** 获取字段 */
				Field field = FieldUtils.getField(beanClass,e.getName(),true);
				Class<?> fieldType;
				ExcelPropertyConfig config;
				if(null != field){
					/**
					 * 如果反射方式能找到字段,则优先根据字段的 ExcelColumn 注解来获取excel导出定义
					 */
					ExcelColumns annots = field.getAnnotation(ExcelColumns.class);
					ExcelColumn[] array = new ExcelColumn[]{};
					if(null == annots){
						ExcelColumn annot = field.getAnnotation(ExcelColumn.class);
						if(null != annot){
							array = new ExcelColumn[]{annot};
						}
					}else {
						array = annots.value();
					}
					if(array.length>0){
						/**  一个Field允许定义多个ExcelColumn注解，但有且只能有一个不包含.的主字段定义,其他为子成员字段定义 */
						int primaryColumnCount=0;
						ExcelPropertyConfig primaryConfig = null;
						for(ExcelColumn annot: array){
							if(annot.columnName().indexOf('.')>0){
								/** 子成员字段 */
								output.add(new ExcelPropertyConfig(annot));	
							}else{
								checkArgument( ++primaryColumnCount <= 1,"only  one ExcelColumn whth pramiary column name required");
								if(!annot.readMethod().isEmpty() && !annot.writeMethod().isEmpty()){
									/**
									 * 如果注解中定义了read/write方法则优先使用定义的方法
									 */
									try {
										PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), beanClass,annot.readMethod(),annot.writeMethod());
										output.add(primaryConfig = new ExcelPropertyConfig(descriptor,nestedName));
									} catch (IntrospectionException e1) {
										throw new RuntimeException(e1);
									}
								}else if(annot.readMethod().isEmpty() && annot.writeMethod().isEmpty()){
									output.add(primaryConfig = new ExcelPropertyConfig(annot,nestedName));
								}else{
									throw new IllegalArgumentException("Read/Write method must not be null OR all be null");
								}
							}	
						}
						config = checkNotNull(primaryConfig,"NOT FOUND  @ExcelColumn for primary column ");
					}else{
						output.add(config = new ExcelPropertyConfig(e,nestedName));
					}
					RowMetaData rowMetaData = RowMetaData.getMetaDataUnchecked(beanClass);
					if(null != rowMetaData){
						/** BaseBean使用字段名做显示名 */
						String name = rowMetaData.columnNameOf(rowMetaData.columnIDOf(e.getName()));
						if(config.getColumnConfig().getName().isEmpty()){
							config.getColumnConfig().setName(name);
						}
					}
					fieldType = field.getType();
				}else {
					/**
					 * 如果反射方式不能找到字段,则优先根据read/write方法定义 ExcelColumn 注解来获取excel导出定义
					 */
					output.add(config = new ExcelPropertyConfig(e,nestedName));
					fieldType = e.getPropertyType();
				}
				if(mayBeJavaBean(fieldType)){
					/** 如果有读写字段定义则递归 */
					if(input instanceof Class){
						fetchExcelPropertyConfigs0(
								fieldType,
								output,
								nestedName+".",
								SheetConfig::fetchProperties);
					}else if(input instanceof Map){
						fetchExcelPropertyConfigs0(
								((Map)input).get(config.getNestedName()),
								output,
								nestedName+".",
								SheetConfig::fetchProperties);
					}else {
						fetchExcelPropertyConfigs0(
								config.readFrom(input),
								output,
								nestedName+".",
								SheetConfig::fetchProperties);
					}	
				}else if(Map.class.isAssignableFrom(fieldType) && !(input instanceof Class)){
					/** 从Map中读取nestedName指定的字段 */
					Map m = (Map)config.readFrom(input);
					fetchExcelPropertyConfig(m, output, nestedName+".");
				}
			});
		}
		//SimpleLog.log("{}",Iterables.toString(Lists.transform(output, c->c.getNestedName())));

		return output;
	}
	private static List<PropertyConfig> fetchExcelPropertyConfigs(Class<?> beanClass,List<PropertyConfig> output,String prefix){
		return fetchExcelPropertyConfigs0(beanClass,output,prefix,SheetConfig::fetchProperties);
	}
	private static List<PropertyConfig> fetchExcelPropertyConfigs(Object bean,List<PropertyConfig> output,String prefix){
		return fetchExcelPropertyConfigs0(bean,output,prefix,SheetConfig::fetchProperties);
	}
	private static List<PropertyConfig> fetchExcelPropertyConfigs(RowMetaData  metaData,List<PropertyConfig> output,String prefix){
		return fetchExcelPropertyConfigs0(metaData,output,prefix,SheetConfig::fetchProperties);
	}
	private static List<PropertyConfig> excelPropertyConfigsOf(Class<?> beanClass){
		return fetchExcelPropertyConfigs(beanClass,Lists.newArrayList(),"");
	}
	private static List<PropertyConfig> excelPropertyConfigsOf(RowMetaData metaData){
		return fetchExcelPropertyConfigs(metaData,Lists.newArrayList(),"");	
	}
	private static List<PropertyConfig> excelPropertyConfigsOf(Object bean){
		return fetchExcelPropertyConfigs(bean,Lists.newArrayList(),"");
	}
 
	@SuppressWarnings("rawtypes")
	private static List<PropertyConfig> excelPropertyConfigsOf(Iterable<Map> iterable){
		LinkedHashMap<String,PropertyConfig>output = Maps.newLinkedHashMap();
		if(null != iterable){
			iterable.forEach(bean->
				fetchExcelPropertyConfigs(bean,Lists.newArrayList(),"").forEach(p->output.putIfAbsent(p.getNestedName(), p)	)
			);
		}
		return Lists.newArrayList(output.values());
	}
	
	private static short getValueOfThriftField(Member member){
		if(member instanceof Method){
			ThriftField thriftField = ((Method)member).getAnnotation(ThriftField.class);
			if(null != thriftField){
				return thriftField.value();
			}
		}else if(member instanceof Field){
			ThriftField thriftField = ((Field)member).getAnnotation(ThriftField.class);
			if(null != thriftField){
				return thriftField.value();
			}
		}
		return Short.MIN_VALUE;
	}
	private static ExcelColumn[] fetchExcelColumns(Class<?> beanClass){
		if(null != beanClass){
			ExcelColumn excelColumn = beanClass.getAnnotation(ExcelColumn.class);
			if(null != excelColumn){
				return new ExcelColumn[]{excelColumn};
			}
			ExcelColumns excelColumns =  beanClass.getAnnotation(ExcelColumns.class);
			if(null != excelColumns){
				return excelColumns.value();
			}
		}
		return new ExcelColumn[0];
	}
	public static boolean mayBeJavaBean(Class<?>type){
		if(null == type){
			return false;
		}else if(Iterable.class.isAssignableFrom(type)){
			return false;
		}else if(Map.class.isAssignableFrom(type)){
			return false;
		}else if(type.isPrimitive()){
			return false;
		}else if(Primitives.unwrap(type).isPrimitive()){
			return false;
		}else if(Date.class.isAssignableFrom(type)){
			return false;
		}else if(UnnameRow.class.isAssignableFrom(type)){
			return false;
		}else if(null!= type.getPackage()&&type.getPackage().getName().matches("^javax?\\..+")){
			return false;
		}
		return true;
	}
}
